package com.cyf.filmDownload.m3u8.task;

import com.cyf.filmDownload.common.util.FileUtil;
import com.cyf.filmDownload.config.AppConfig;
import com.cyf.filmDownload.m3u8.core.M3u8DownloadUtil;
import com.cyf.filmDownload.m3u8.entity.M3u8ParserResult;
import com.cyf.filmDownload.m3u8.entity.VideoTs;
import com.cyf.filmDownload.m3u8.exception.M3u8Exception;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Data
@Slf4j
public class ComposeTask implements Runnable {

    /**
     * 记录视频重试次数
     */
    private static ConcurrentHashMap<String, AtomicInteger> retryCountMap = new ConcurrentHashMap<>();

    private M3u8ParserResult parserResult;

    public ComposeTask(){
    }

    public ComposeTask(M3u8ParserResult parserResult) {
       this.parserResult = parserResult;
    }

    @Override
    public void run() {
        String tsDirPath = parserResult.getTsDir();
        List<VideoTs> tsList = parserResult.getTsList();
        String videoName = parserResult.getVideoName();

        File tsDir = new File(tsDirPath);
        File[] files = tsDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".ts"));
        if (ArrayUtils.isEmpty(files)) {
            retryCountMap.remove(videoName);
            throw new M3u8Exception("ts目录：" + tsDirPath + " 无下载的ts文件");
        }

        // 检查 ts 数量是否一致
        if (files.length >= parserResult.getTsCount()) {
            composeFile(parserResult.getTsDir(), parserResult.getVideoPath(), parserResult.getTempMainDir());
            if (AppConfig.isDeleteTsTempDir) {
                FileUtils.deleteQuietly(new File(parserResult.getTempMainDir()));
            }
            log.info("{}下载结束，地址：{}", videoName, parserResult.getVideoPath());
            retryCountMap.remove(videoName);
            return;
        }

        // 寻找下载失败的 ts 列表
        log.error("ts目录：{} 数量不一致，预期数量：{}，实际数量：{}", tsDirPath, parserResult.getTsCount(), files.length);
        Set<String> fileNameSet = Arrays.asList(files).stream().map(File::getName).collect(Collectors.toSet());
        List<VideoTs> failTsList = tsList.stream().filter(ts -> !fileNameSet.contains(ts.getFileName())).collect(Collectors.toList());
        parserResult.setTsList(failTsList);
        AtomicInteger retryCount = retryCountMap.computeIfAbsent(videoName, k -> new AtomicInteger(0));
        if (retryCount.incrementAndGet() > AppConfig.tsDownloadRetryCount) {
            log.error("{} 下载失败，重试次数过多，还有 {} 个分片未下载", videoName, failTsList.size());;
            retryCountMap.remove(videoName);
            return;
        }
        log.info("视频：{}，第 {} 次尝试下载剩余的视频分片，还有 {} 个分片未下载", videoName, retryCount, failTsList.size());
        M3u8DownloadUtil.downloadTs(parserResult);
    }

    /**
     * 将指定目录下的TS文件合并为MP4文件
     * @param tsDirPath TS文件所在目录路径
     * @param videoPath 输出的MP4文件路径
     * @return true表示合并成功，false表示失败
     */
    public static void composeFile(String tsDirPath, String videoPath, String tempDir) {
        File tsDir = new File(tsDirPath);
        File[] tsFiles = tsDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".ts"));
        FileUtil.createFile(videoPath);

        // 1. 创建临时文件列表
        File listFile = FileUtil.createFile(Paths.get(tempDir, "ffmpeg-list-file.txt").toString());

        // 2. 写入文件列表
        try (FileWriter writer = new FileWriter(listFile)) {
            for (File tsFile : tsFiles) {
                writer.write("file '" + tsFile.getAbsolutePath() + "'\n");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 3. 构建FFmpeg命令
        List<String> command = new ArrayList<>();
        command.add(AppConfig.ffmpegPath);
        command.add("-f");
        command.add("concat");
        command.add("-safe");
        command.add("0"); // 允许任意路径
        command.add("-i");
        command.add(listFile.getAbsolutePath());
        command.add("-c");
        command.add("copy");
        command.add("-bsf:a");
        command.add("aac_adtstoasc");
        command.add("-y");
        command.add(videoPath);

        // 执行命令...
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        processBuilder.redirectErrorStream(true); // 合并标准错误和标准输出

        try {
            Process process = processBuilder.start();
            // 读取输出流
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            }

            int exitCode = process.waitFor();
            if (exitCode == 0) {
                log.info("合并成功! 输出文件: {}", videoPath);
            } else {
                log.error("合并失败，FFmpeg退出码: {}", exitCode);
            }

        } catch (IOException | InterruptedException e) {
            throw new RuntimeException("执行视频合并命令时出错", e);
        }
    }
}